import { DependencyContainer, convertFilterParameters, usePlugin, IFilter } from "@sker/core"
import { useReqBody } from '@sker/runtime'
import { FindOptionsWhere, ObjectLiteral, FindOptionsUtils, SelectQueryBuilder, Brackets, WhereExpressionBuilder, FindManyOptions } from "typeorm";
import { z } from "zod"
const ConditionalFilter = z.object({
    key: z.optional(z.string()),
    operator: z.enum(['and', 'or']),
    value: z.array(z.any())
})
const LogicalFilter = z.object({
    field: z.string(),
    operator: z.enum(['eq', 'ne', 'lt', 'gt', 'lte', 'gte', 'nin', 'contains', 'ncontains', 'containss', 'ncontainss', 'between', 'nbetween', 'null', 'nnull', 'startswith', 'nstartswitch', 'endswitch', 'nendswith', 'endswiths', 'nendswiths']),
    value: z.any()
});
const Pagination = z.object({
    current: z.optional(z.number()),
    pageSize: z.optional(z.number())
})
const MetaQuery = z.object({
    fields: z.optional(z.array(z.string().or(z.any()))),
})
const Sorters = z.array(z.object({
    field: z.string(),
    order: z.enum(['asc', 'desc'])
}))
const Filters = z.array(ConditionalFilter.or(LogicalFilter))
const schema = z.object({
    resource: z.string(),
    sorters: z.optional(Sorters),
    filters: z.optional(Filters),
    pagination: z.optional(Pagination),
    meta: MetaQuery
});
function filtersToIFilter<T>(filters: z.infer<typeof Filters>): IFilter<T> {
    if(!filters) return {} as IFilter<T>;
    const filter: IFilter<T> = {} as IFilter<T>;
    filters.map(f => {
        switch (f.operator) {
            case 'and':
                return Reflect.set(filter, 'and', filtersToIFilter(f.value))
            case 'or':
                return Reflect.set(filter, 'or', filtersToIFilter(f.value))
            default:
                return Reflect.set(filter, f.field, { [`${f.operator}`]: f.value })
        }
    })
    return filter;
}
export default async (injector: DependencyContainer) => {
    const body = schema.parse(useReqBody())
    const res = await usePlugin('@sker/orm', async (ds) => {
        const metadata = ds.getMetadata(body.resource);
        if (!metadata) throw new Error(`${body.resource}没有找到`)
        const fields = body.meta?.fields || [];
        const pagination = body.pagination;
        const sorters = sortersToOrder(body.sorters || []);
        const current = pagination?.current || 1;
        const psize = pagination?.pageSize || 20;
        const { select, relations } = feildsToSelect(fields)
        const where = convertFilterParameters(filtersToIFilter(body.filters))
        console.log({where})
        const options: FindManyOptions<ObjectLiteral> = {
            select: select,
            relations: relations,
            take: psize,
            skip: (current - 1) * psize,
            order: sorters,
            where: where,
        }
        const queryBuilder = ds.manager.createQueryBuilder(metadata.target, FindOptionsUtils.extractFindManyOptionsAlias(options) || metadata.name)
            .setFindOptions(options || {})
        const [list, total] = await queryBuilder.getManyAndCount()
        return { success: true, data: list, total };
    })
    return res;
}
function sortersToOrder(sorters: z.infer<typeof Sorters>) {
    return sorters.reduce((a, b) => {
        return {
            ...a,
            [`${b.field}`]: b.order
        }
    }, {})
}
function feildsToSelect(fields: any[]) {
    if(!fields) return;
    const select = {};
    const relations = {};
    fields.map(it => {
        if (typeof it === 'string') {
            Reflect.set(select, it, true)
        } else {
            Object.keys(it).map(key => {
                const value = Reflect.get(it, key)
                const { select: childSelect, relations: childRelations } = feildsToSelect(value)
                Reflect.set(relations, key, childRelations)
                Reflect.set(select, key, childSelect)
            })
        }
    })
    return {
        select,
        relations: Object.keys(relations).length > 0 ? relations : true,
    }
}